/**
 * 
 */
package main;

import interfaces.ILinearEquation;

import java.awt.geom.Line2D;

import neuralnetwork.SmoothingAlgorithm;

import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import tools.PolynomialGenerator;
import tools.UtilFunctions;

/**
 * @author Vivek
 * 
 */
public class PathLengthNormalisedSmoothing {

	public Line2D.Double[] lines;
	double arcLength;
	static final Logger logger = Logger
			.getLogger(PathLengthNormalisedSmoothing.class); // Setup logging

	public PathLengthNormalisedSmoothing(double[][] xy) {
		// loading logging properties
		PropertyConfigurator.configure("Logging Properties/log4j.properties");
		setup(xy);
	}

	private void setup(double[][] xy) {

		ILinearEquation lEq = new PolynomialGenerator(xy);
		logger.setLevel(Level.FATAL);
		lEq.solveEquation();
		logger.info(UtilFunctions.display2DArray(xy));
		double[][] xy_predicted = lEq.getPredicted(2);
		logger.info("Predicted:" + UtilFunctions.display2DArray(xy_predicted));
		
		PolynomialFunction univariate= new PolynomialFunction(((PolynomialGenerator) lEq).solution.toArray());
		this.arcLength = getPolynomialArcLength(univariate,
				xy_predicted[0][0], xy_predicted[xy_predicted.length-1][0], 200);

		
		lines = new Line2D.Double[xy_predicted.length - 1];
		for (int n = 1; n < xy_predicted.length; n++) {
			lines[n - 1] = new Line2D.Double(xy_predicted[n - 1][0],
					xy_predicted[n - 1][1], xy_predicted[n][0],
					xy_predicted[n][1]);
		}
	}

	
	public double calculateSmoothness() {

		double smoothnessValue = 0d;
		for (int n = 1; n < lines.length; n++) {
			double length = lines[n - 1].getP2().distance(lines[n - 1].getP1());
			smoothnessValue += Math.pow(
					angleBetweenLines(lines[n - 1], lines[n]), 2)
					* length;
		}
		return smoothnessValue/this.arcLength;
	}

	public double angleBetweenLines(Line2D.Double line1, Line2D.Double line2) {
		return Math
				.atan(((slope(line2) - slope(line1)) / (1 + (slope(line2) * slope(line1)))));
	}

	public double slope(Line2D.Double line) {
		return ((line.y2 - line.y1) / (line.x2 - line.x1));
	}



	/**
	 * This function is used to calculate calculate the arc length of an
	 * polynomial. Specifically this function creates an array of points on
	 * curve at regular intervals (that is passed as parameters)
	 * 
	 * Its makes a call to getPolynomialLength to calculate the sum length for
	 * all the points.
	 * 
	 * @param univariate
	 *            Polynomial function whose arc length needs to be calculates
	 * @param start
	 *            X coordinate for the start point.
	 * @param end
	 *            X coordinates for end point.
	 * @param interval
	 *            total number of interval to be considered for calculation of
	 *            arc length
	 * @return
	 */
	private double getPolynomialArcLength(PolynomialFunction univariate,
			double start, double end, int interval) {
		double step = (double) (end - start) / interval;
		double[][] coord = new double[1 + interval][2];

		for (int counter = 0; counter <= interval; counter++) {
			coord[counter][0] = start + counter * step;
			coord[counter][1] = univariate.value(coord[counter][0]);

		}
		// System.out.println(Arrays.toString(coord[0]));
		// System.out.println(Arrays.toString(coord[1]));
		// System.out.println("Lenght"+coord.length+" Step:"+step);
		return getPolynomialLength(coord);
	}

	public static double getPolynomialLength(double[][] xy) {
		double length[] = new double[xy.length - 1];

		for (int n = 1; n < xy.length; n++) {
			length[n - 1] = Math.sqrt(Math.pow((xy[n - 1][0] - xy[n][0]), 2)
					+ Math.pow((xy[n - 1][1] - xy[n][1]), 2));
			// System.out.println("HEre :"+length[n-1]);
		}
		double sum = 0;
		for (double i : length) {
			sum += i;
		}

		return sum;
	}

	


}
